Step 34: Validate ObjectID

We should be validating the input to Bookmark DAO operations. Namely, we should validate the ID parameter.

Add the following imports at the top of src/data/BookmarkDAO.js file:

import { z } from "zod";
import mongoose from "mongoose";

Update the following operation in the same file

// return the bookmark with the given id
// return null if resource does not exist in our database
// throws ApiError if id is invalid
async read(id) {
  try {
    z.string()
      .refine((id) => mongoose.isValidObjectId(id), {
        message: "Invalid ID!",
      })
      .parse(id);
    const bookmark = await Bookmark.findById(id);
    return bookmark;
  } catch (err) {
    throw new ApiError(400, err.message);
  }
}

Update the following tests in the tests/data/BookmarkDAO.test.js file”

it("test read() given invalid ID", async () => {
  try {
    await bookmarkDAO.read("invalid");
  } catch (err) {
    expect(err.status).toBe(400);
  }
});

it("test read() given valid but non-existing ID", async () => {
  const _bookmark = await bookmarkDAO.read(
    mongoose.Types.ObjectId().toString()
  );
  expect(_bookmark).toBeNull();
});

Let’s update the BookmarkDAO.js to validate object IDs for other operations too. First, refactor the code by adding the following helper function before the class declaration.

const validObjectId = z.string().refine((id) => mongoose.isValidObjectId(id), {
  message: "Invalid ID!",
});

Next, update the read operation:

// return the bookmark with the given id
// return null if resource does not exist in our database
// throws ApiError if id is invalid
async read(id) {
  try {
    validObjectId.parse(id);
    const bookmark = await Bookmark.findById(id);
    return bookmark;
  } catch (err) {
    throw new ApiError(400, err.message);
  }
}

Now update the delete and update operations and add tests for them!

Run all the tests and make sure they all pass. Then, save and commit changes.